// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_resource_manager.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_render_device.h"
#include "mk_render_define.h"
#include "mk_texture.h"
#include "mk_mesh.h"
#include "mk_shader.h"
#include "mk_ft_font.h"
#include "mk_skeleton_data.h"
#include "mk_image.h"
#include "mk_hardware_buffer.h"

namespace MK3DE
{
	CResourceManager::CResourceManager()
	{

	}

	CResourceManager::~CResourceManager()
	{
		Destroy();
	}

	BOOL CResourceManager::Create()
	{
		BOOL ret = FALSE;

		// Initialize FreeType lib.
		ret = CFTFont::Create();
		MK_PROCESS_ERROR(ret);

		// Initialize DevIL lib.
		ilInit();

		ret = TRUE;
Exit0:
		return ret;
	}

	void CResourceManager::Destroy()
	{
		RemoveAllResource();
		CFTFont::Destroy();
	}

	BOOL CResourceManager::GetTexture2DFromFile(const char* fileName, CTexture2D*& pTexture)
	{
		CTexture2D* tex = (CTexture2D*)GetResourceByName(RES_T_TEXTURE2D, fileName);
		if (tex)
		{
			pTexture = tex;
			tex->AddRef();
			return TRUE;
		}

		GetEngine()->GetLog()->Write(MKLT_INFO, "Load texture2D [%s]", fileName);
		BOOL ret = FALSE;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		ret = rd->CreateTexture2DFromFile(fileName, tex);
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_TEXTURE2D][std::string(fileName)] = (CResource*)tex;
		pTexture = tex;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetTexture2DFromMemory(const BYTE buffer[], DWORD size, CTexture2D*& pTexture)
	{
		GetEngine()->GetLog()->Write(MKLT_INFO, "Load texture from memory[size: %d]", size);
		BOOL ret = FALSE;
		CTexture2D* tex = NULL;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		ret = rd->CreateTexture2DFromMemory(buffer, size, tex);
		MK_PROCESS_ERROR(ret);

		_namelessResList[RES_T_TEXTURE2D].push_back((CResource*)tex);

		pTexture = tex;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetTexture2D(DWORD width, DWORD height, PIXEL_FORMAT format, DWORD mip, TEXTURE_USAGE usage, CTexture2D*& pTexture)
	{
		BOOL ret = FALSE;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		CTexture2D* tex = NULL;
		ret = rd->CreateTexture2D(width, height, format, mip, usage, tex);
		MK_PROCESS_ERROR(ret);
		_namelessResList[RES_T_TEXTURE2D].push_back((CResource*)tex);
		pTexture = tex;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetTexture3DFromFile(const char* fileName, CTexture3D*& pTexture)
	{
		CTexture3D* tex = (CTexture3D*)GetResourceByName(RES_T_TEXTURE3D, fileName);
		if (tex)
		{
			pTexture = tex;
			tex->AddRef();
			return TRUE;
		}

		CEngine* engine = GetEngine();
		engine->GetLog()->Write(MKLT_INFO, "Load texture3D [%s]", fileName);

		BOOL ret = engine->GetRenderDevice()->CreateTexture3DFromFile(fileName, tex);
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_TEXTURE3D][std::string(fileName)] = (CResource*)tex;
		pTexture = tex;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetMesh(const char* fileName, CMesh*& pMesh)
	{
		CMesh* mesh = (CMesh*)GetResourceByName(RES_T_MESH, fileName);
		if (mesh)
		{
			pMesh = mesh;
			pMesh->AddRef();
			return TRUE;
		}

		GetEngine()->GetLog()->Write(MKLT_INFO, "Load mesh [%s]", fileName);
		BOOL ret = FALSE;
		mesh = new CMesh(fileName);
		MK_PROCESS_ERROR(mesh);

		ret = mesh->Load();
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_MESH][std::string(fileName)] = (CResource*)mesh;
		pMesh = mesh;

		ret = TRUE;

Exit0:
		return ret;
	}

	BOOL CResourceManager::GetShaderFromFile(const char* fileName, CShader*& pShader)
	{
		CShader* shader = (CShader*)GetResourceByName(RES_T_SHADER, fileName);
		if (shader)
		{
			pShader = shader;
			shader->AddRef();
			return TRUE;
		}

		GetEngine()->GetLog()->Write(MKLT_INFO, "Load shader [file name: %s]", fileName);
		BOOL ret = FALSE;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		ret = rd->CreateShaderFromFile(fileName, shader);
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_SHADER][std::string(fileName)] = (CResource*)shader;
		pShader = shader;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetShaderFromMemory(const BYTE buffer[], DWORD size, CShader*& pShader)
	{
		GetEngine()->GetLog()->Write(MKLT_INFO, "Load shader from memory[size: %d]", size);
		BOOL ret = FALSE;
		CShader* shader = NULL;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		ret = rd->CreateShaderFromMemory(buffer, size, shader);
		MK_PROCESS_ERROR(ret);

		_namelessResList[RES_T_SHADER].push_back((CResource*)shader);

		pShader = shader;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetFTFont(const char* fileName, BOOL bAntialiased, DWORD points, CFTFont*& pFont)
	{
		char name[1024];
		sprintf(name, "%s_%d_%d", fileName, bAntialiased, points);
		CFTFont* font = (CFTFont*)GetResourceByName(RES_T_FTFONT, name);
		if (font)
		{
			pFont = font;
			font->AddRef();
			return TRUE;
		}

		GetEngine()->GetLog()->Write(MKLT_INFO, "Load FreeType font [%s]", fileName);
		BOOL ret = FALSE;
		font = new CFTFont(fileName, bAntialiased, points);
		MK_PROCESS_ERROR(font);

		ret = font->Load();
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_FTFONT][std::string(name)] = (CFTFont*)font;
		pFont = font;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetSkeletonData(const char* fileName, CSkeletonData*& pSkeletonData)
	{
		CSkeletonData* skeleton = (CSkeletonData*)GetResourceByName(RES_T_SKELETON_DATA, fileName);
		if (skeleton)
		{
			pSkeletonData = skeleton;
			pSkeletonData->AddRef();
			return TRUE;
		}

		BOOL ret = FALSE;
		GetEngine()->GetLog()->Write(MKLT_INFO, "Load skeleton data [file: %s]", fileName);
		pSkeletonData = new CSkeletonData(fileName);
		MK_PROCESS_ERROR(pSkeletonData);

		ret = pSkeletonData->Load();
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_SKELETON_DATA][std::string(fileName)] = (CResource*)pSkeletonData;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetImage(const char* fileName, CImage*& pImage)
	{
		CImage* image = (CImage*)GetResourceByName(RES_T_IMAGE, fileName);
		if (image)
		{
			pImage = image;
			pImage->AddRef();
			return TRUE;
		}

		BOOL ret = FALSE;
		GetEngine()->GetLog()->Write(MKLT_INFO, "Load image [file: %s]", fileName);
		pImage = new CImage(fileName);
		MK_PROCESS_ERROR(pImage);

		ret = pImage->Load();
		MK_PROCESS_ERROR(ret);

		_resMapList[RES_T_IMAGE][std::string(fileName)] = (CResource*)pImage;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetVertexBuffer(DWORD vertexSize, DWORD vertexNum, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& vertexBuffer)
	{
		BOOL ret = FALSE;
		CRenderDevice* renderDevice = GetEngine()->GetRenderDevice();
		CHardwareBuffer* buffer = NULL;
		ret = renderDevice->CreateVertexHardwareBuffer(vertexSize, vertexNum, usage, buffer);
		MK_PROCESS_ERROR(ret);

		_namelessResList[RES_T_HARDWARE_BUFFER].push_back((CResource*)buffer);
		vertexBuffer = buffer;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::GetIndexBuffer(DWORD indexNum, INDEX_TYPE indexType, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& indexBuffer)
	{
		BOOL ret = FALSE;
		CRenderDevice* renderDevice = GetEngine()->GetRenderDevice();
		CHardwareBuffer* buffer = NULL;
		ret = renderDevice->CreateIndexHardwareBuffer(indexNum, indexType, usage, buffer);
		MK_PROCESS_ERROR(ret);

		_namelessResList[RES_T_HARDWARE_BUFFER].push_back((CResource*)buffer);
		indexBuffer = buffer;

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CResourceManager::OnResourceDelete(CResource* res)
	{
		RESOURCE_TYPE type = res->GetType();
		std::string resFileName = res->GetResourceName();
		GetEngine()->GetLog()->Write(MKLT_INFO, "Release resource type: [%d] file: [%s]", type, resFileName.c_str());
		if (resFileName.size() == 0)
		{
			std::list<CResource*>& resList = _namelessResList[type];
			std::list<CResource*>::iterator i = resList.begin();
			while (i != resList.end())
			{
				if (res == (*i))
				{
					resList.erase(i);
					return TRUE;
				}

				++i;
			}
		}
		else
		{
			stdext::hash_map<std::string, CResource*>& resMap = _resMapList[type];
			stdext::hash_map<std::string, CResource*>::iterator i = resMap.find(resFileName);
			if (i != resMap.end())
			{
				resMap.erase(i);
				return TRUE;
			}
		}

		return FALSE;
	}

	BOOL CResourceManager::RemoveAllResource()
	{
		for (int i = 0; i < (int)RES_T_MAX; ++i)
		{
			stdext::hash_map<std::string, CResource*>& resMap = _resMapList[i];
			stdext::hash_map<std::string, CResource*>::iterator it = resMap.begin();
			while (it != resMap.end())
			{
				CResource* res = (CResource*)(it->second);
				GetEngine()->GetLog()->Write(MKLT_INFO, "Release resource type: [%d] file: [%s]", res->GetType(), res->GetResourceName().c_str());
				SAFE_DELETE(res);

				++it;
			}
			resMap.clear();
		}

		std::list<CResource*>::iterator it = _namelessResList->begin();
		while (it != _namelessResList->end())
		{
			CResource* res = (CResource*)*it;
			SAFE_DELETE(res);

			++it;
		}
		_namelessResList->clear();

		return TRUE;

	}

	CResource* CResourceManager::GetResourceByName(RESOURCE_TYPE type, const char* name)
	{
		stdext::hash_map<std::string, CResource*>& resMap = _resMapList[type];
		stdext::hash_map<std::string, CResource*>::iterator i = resMap.find(std::string(name));
		if (i != resMap.end())
			return i->second;
		return NULL;
	}

	void CResourceManager::ReloadAllResource()
	{
		CLog* log = GetEngine()->GetLog();
		for (int i = 0; i < (int)RES_T_MAX; ++i)
		{
			stdext::hash_map<std::string, CResource*>& resMap = _resMapList[i];
			stdext::hash_map<std::string, CResource*>::iterator it = resMap.begin();
			while (it != resMap.end())
			{
				CResource* res = (CResource*)(it->second);
				BOOL ret = res->Reload();
				if (!ret)
					log->Write(MKLT_ERROR, "Reload resource[%s] failed", res->GetResourceName());

				++it;
			}
		}
	}

	void CResourceManager::ReloadResource(RESOURCE_TYPE type)
	{
		CLog* log = GetEngine()->GetLog();
		stdext::hash_map<std::string, CResource*>& resMap = _resMapList[type];
		stdext::hash_map<std::string, CResource*>::iterator it = resMap.begin();
		while (it != resMap.end())
		{
			CResource* res = (CResource*)(it->second);
			if (!res->Reload())
				log->Write(MKLT_ERROR, "Reload resource[%s] failed", res->GetResourceName());

			++it;
		}
	}
}